#region Microsoft Public License
/*
 * (c) Copyright Chenjun Wu. http://utablesdk.codeplex.com
 * 
 * This source is subject to the Microsoft Public License (Ms-PL).
 * Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
 * All other rights reserved.
 */
#endregion

﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Media;
using UTable.Objects.Handlers;
using UTable.Input.MultiTouch;
using UTable.Input;

namespace UTable.Objects.Controls
{
	internal class UScanViewPanel: UCanvas
	{
		#region Private Methods

		private TranslateTransform transform = new TranslateTransform();

		private Point dragStartPosition;

		private double dragStartOffset;

		#endregion

		#region Private Methods

		private static void OnOffsetChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
		{
			(obj as UScanViewPanel).OnOffsetChanged();
		}

		private void OnOffsetChanged()
		{
			if (transform != null)
			{
				if (ScanView.TurnDirection == UScanViewTurnDirectionType.LeftRight)
					transform.X = Offset;
				else
					transform.Y = Offset;
			}
		}

		private void Clip()
		{
			if (ScanView != null)
			ScanView.ClipToBounds = true;
			for (int i = 0; i < this.Children.Count; i++)
			{
				this.Children[i].Visibility = Visibility.Visible;
			}
		}

		private void NotClip()
		{
			if (ScanView != null)
				ScanView.ClipToBounds = false;
			for (int i=0; i<this.Children.Count; i++)
			{
				if (i != CurrentItemIndex)
					this.Children[i].Visibility = Visibility.Hidden;
				else
					this.Children[i].Visibility = Visibility.Visible;
			}
		}

		#endregion

		#region MultiTouchProcessor

		public void InitializeMultiTouchProcessorPart()
		{
			this.InputReceived += new InputEventHandler(MultiTouchProcessor_InputReceived);
			MultiTouchProcessor = new MultiTouchProcessor();
			SingleFingerGestureHandler inputHandler = new SingleFingerGestureHandler(this, this.ScanView);
			inputHandler.GestureDetected += new SingleFingerGestureEventHandler(Panel_GestureDetected);
			inputHandler.TrackVelocity = true;
			MultiTouchProcessor.Handlers.Add(inputHandler);
		}

		void MultiTouchProcessor_InputReceived(InputEventArgs args)
		{
			if (args is MultiTouchEventArgs)
				MultiTouchProcessor.RaiseMultiTouchEvent(args as MultiTouchEventArgs);
		}

		public MultiTouchProcessor MultiTouchProcessor { get; set; }

		private void Panel_GestureDetected(SingleFingerGestureHandler sender, SingleFingerGestureEventArgs args)
		{
			switch (args.Type)
			{
				case SingleFingerGestureType.Tap:
					OnTapped(sender.Node as UGridViewItem, args as SingleFingerGestureTapEventArgs);
					break;
				case SingleFingerGestureType.DragStarted:
					OnDragStarted(sender.Node as UGridViewItem, args as SingleFingerGestureDragEventArgs);
					break;
				case SingleFingerGestureType.Dragging:
					OnDragging(sender.Node as UGridViewItem, args as SingleFingerGestureDragEventArgs);
					break;
				case SingleFingerGestureType.DragEnded:
					OnDragEnded(sender.Node as UGridViewItem, args as SingleFingerGestureDragEventArgs);
					break;
			}
		}

		private void OnTapped(UGridViewItem item, SingleFingerGestureTapEventArgs args)
		{

		}

		private void OnDragStarted(UGridViewItem item, SingleFingerGestureDragEventArgs args)
		{
			dragStartPosition = args.FingerPosition;
			dragStartOffset = Offset;
			Offset = Offset;
		}

		private void OnDragging(UGridViewItem item, SingleFingerGestureDragEventArgs args)
		{
			if (ScanView.TurnDirection == UScanViewTurnDirectionType.LeftRight)
				this.Offset = dragStartOffset + args.FingerPosition.X - dragStartPosition.X;
			else
				this.Offset = dragStartOffset + args.FingerPosition.Y - dragStartPosition.Y;
		}

		private void OnDragEnded(UGridViewItem item, SingleFingerGestureDragEventArgs args)
		{
			// determine to turn left or turn right according to the velocity
			if (ScanView.TurnDirection == UScanViewTurnDirectionType.LeftRight)
			{
				if (args.Velocity.X > 0)
					TurnPrevious();
				else
					TurnNext();
			}
			else
			{
				if (args.Velocity.Y > 0)
					TurnPrevious();
				else
					TurnNext();
			}
		}

		#endregion

		#region Protected Methods

		protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
		{
			base.OnVisualChildrenChanged(visualAdded, visualRemoved);

			ArrangeChildren();
		}

		#endregion

		#region Constructors

		public UScanViewPanel()
		{
			transform = new TranslateTransform();
			this.RenderTransform = transform;
			LeftToRight = true;
		}

		#endregion

		#region Properties

		public int CurrentItemIndex
		{
			get 
			{
				if (ScanView == null)
					return 0;
				if (ScanView.TurnDirection == UScanViewTurnDirectionType.LeftRight)
					return (int)(-Offset / this.ActualWidth);
				else
					return (int)(-Offset / this.ActualHeight);
			}
		}

		public UScanView ScanView { get; set; }

		public bool LeftToRight { get; protected set; }

		public double Offset
		{
			get { return (double)GetValue(OffsetProperty); }
			set { SetValue(OffsetProperty, value); }
		}

		public static readonly DependencyProperty OffsetProperty =
			DependencyProperty.Register("Offset", typeof(double), typeof(UScanViewPanel), new UIPropertyMetadata(0.0, new PropertyChangedCallback(OnOffsetChanged)));


		#endregion

		#region Public Methods

		public void OnDirectionChanged()
		{
			if (ScanView.TurnDirection == UScanViewTurnDirectionType.LeftRight)
			{
				int index = (int)(-Offset / this.ActualHeight);
				transform.X = -index * this.ActualWidth;
				transform.Y = 0;
			}
			else
			{
				int index = (int)(-Offset / this.ActualWidth);
				transform.X = 0;
				transform.Y = -index * this.ActualHeight;
			}
			ArrangeChildren();
		}

		public void JumpTo(int index)
		{
			double desOffset;
			TimeSpan duration;
			if (ScanView.TurnDirection == UScanViewTurnDirectionType.LeftRight)
			{
				// get the des offset
				desOffset = -index * this.ActualWidth;

				// get the animation duration
				duration = TimeSpan.FromMilliseconds(500 * Math.Abs(desOffset - Offset) / this.ActualWidth);
			}
			else
			{
				// get the des offset
				desOffset = -index * this.ActualHeight;

				// get the animation duration
				duration = TimeSpan.FromMilliseconds(500 * Math.Abs(desOffset - Offset) / this.ActualHeight);
			}

			AnimationUtility.DoDoubleAnimation(desOffset, duration, UScanViewPanel.OffsetProperty, this);
		}

		public void TurnPrevious()
		{
			// get the index to turn
			int toIndex;
			if (ScanView.TurnDirection == UScanViewTurnDirectionType.LeftRight)
				toIndex = (int)(-Offset / this.ActualWidth);
			else
				toIndex = (int)(-Offset / this.ActualHeight);
			if (toIndex > this.Children.Count - 1)
				toIndex = this.Children.Count - 1;
			if (toIndex < 0)
				toIndex = 0;

			JumpTo(toIndex);
		}

		public void TurnNext()
		{
			// get the index to turn
			int toIndex;
			if (ScanView.TurnDirection == UScanViewTurnDirectionType.LeftRight)
				toIndex = (int) ((-Offset + this.ActualWidth) / this.ActualWidth);
			else
				toIndex = (int)((-Offset + this.ActualHeight) / this.ActualHeight); 
			if (toIndex > this.Children.Count - 1)
				toIndex = this.Children.Count - 1;
			if (toIndex < 0)
				toIndex = 0;

			JumpTo(toIndex);
		}

		public void ArrangeChildren()
		{
			// NotClip();

			double item_width = this.ActualWidth;
			double item_height = this.ActualHeight;

			if (ScanView != null && ScanView.TurnDirection == UScanViewTurnDirectionType.LeftRight)
			{
				double posX = 0;

				for (int i = 0; i < this.Children.Count; i++)
				{
					UScanViewItem child = this.Children[i] as UScanViewItem;
					child.Width = item_width;
					child.Height = item_height;
					Canvas.SetLeft(child, posX);
					Canvas.SetTop(child, 0);
					posX += item_width;
				}
			}
			else
			{
				double posY = 0;

				for (int i = 0; i < this.Children.Count; i++)
				{
					UScanViewItem child = this.Children[i] as UScanViewItem;
					child.Width = item_width;
					child.Height = item_height;
					Canvas.SetLeft(child, 0);
					Canvas.SetTop(child, posY);
					posY += item_height;
				}
			}
		}

		#endregion
	}
}
